/**
 * Copyright (C)  Gadglet .
 *
 * This file is part of Gadglet
 *
 * Gadglet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Gadglet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Gadglet. If not, see <http://www.gnu.org/licenses/>.
 */


package com.gadglet.core;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;

import com.gadglet.params.FieldErrorTypes;
import com.gadglet.params.ReqErrorTypes;
import com.gadglet.params.ReqParamNames;
import com.gadglet.params.ResParamNames;
import com.gadglet.params.ResStatus;
import com.gadglet.params.SharingType;



/**
 * 
 * This class represent a response sent to the client in JOSN format
 * The class is parsed to JOSN on the server using Gson
 * 
 * Sample JSON representation:
 * 
 * {\"gadgetServerResponse\":
 * 		{\"responseHeader\":
 * 			{\"responseStatus\":\"ok\",
 * 			\"userContainer\":\"www.google.com\",
 * 			\"responseVersion\":\"1.0\",
 * 			\"hasMoreItems\":false,
 * 			\"totalItemsNum\":2,
 * 			\"addPermit\":true,
 * 			\"errorFields\":[],
 * 			\"customHeaders\":[]},
 * 		\"responseItems\":
 * 			[{\"item\":
 * 				{\"field1\":\"????????\",
 * 				\"field2\":\"????\",
 * 				\"gadgetContentItemKey\":\"agp5dXZpc2FtcGxlchkLEhFQZXJzb25hbE5vdGVzRGF0YRiL3BgM\"},
 * 				\"itemPermissions\":
 * 						{\"updatePermit\":true,
 * 						\"deletePermit\":true,
 * 						\"sharePermit\":false}},
 * 			{\"item\":
 * 				{\"field1\":\"????????\",
 * 				\"field2\":\"????\",
 * 				\"gadgetContentItemKey\":\"agp5dXZpc2FtcGxlchkLEhFQZXJzb25hbE5vdGVzRGF0YRiL3BgM\"},
 * 				\"itemPermissions\":
 * 						{\"updatePermit\":true,
 * 						\"deletePermit\":true,
 * 						\"sharePermit\":false}},
 * 			]}}
 * 
 * 
 * 
 */
public class GadgletResponse {

	// DO NOT REFACTOR NAMES, DON'T ADD more MEMBERS -- need to change
	// ResParamNames and compile GWT client
	private final MessageHeader responseHeader = new MessageHeader();
	private final ArrayList<ContentRecord> responseItems = new ArrayList<ContentRecord>();

	public GadgletResponse() {

		responseHeader.setResponseStatus(ResStatus.OK);
	}

	/**
	 * @return
	 * Use by server developer to notify client if there are more results in case 
	 * of paging 
	 */
	public boolean hasMore() {
		return responseHeader.isHasMore();
	}

	
	/**
	 * @param fieldName - name of the field with the error
	 * @param fieldErrorType - error type will be translated on the client to text using Gadget API
	 */
	public void addFieldError(String fieldName,
			FieldErrorTypes fieldErrorType) {
		responseHeader.addFieldError(fieldName,
				fieldErrorType.getErrorMessage());
	}

	/**
	 * @param fieldName - name of the field with the error
	 * @param fieldErrorType - String that described the error
	 */
	public void addCustomFieldError(String fieldName, String fieldErrorMsg) {
		responseHeader.addFieldError(fieldName, fieldErrorMsg);
	}

	public void setUserContainer(String userContainer){
		responseHeader.setUserContainer(userContainer);
	}
	
	
	/**
	 * Enables developer to add custom headers fields
	 * @param headerName 
	 * @param headerValue
	 */
	public void addCustomHeader(String headerName, String headerValue) {
		responseHeader.addCustomeHeader(headerName, headerValue);
	}

	
	/**
	 * @return a new content record 
	 */
	public ContentRecord newItem() {
		ContentRecord rec = new ContentRecord();
		responseItems.add(rec);

		return rec;

	}

	/** 
	 * A simple way for the server side to "tell" the client if the current user
	 * is permitted to add content. Can be used on the client to control UI.
	 * @param permit
	 */
	public void setAddPermit(boolean permit) {
		responseHeader.setAddPermit(permit);
	}

	
	/**
	 * A simple way for the server to "tell" the client that there are more
	 * items to present and possibly a "next" button/link should be enabled.
	 * @param hasMore
	 */
	public void setHasMore(boolean hasMore) {
		responseHeader.setHasMore(hasMore);
	}

	
	/**
	 * Server method to "tell" the client an error has happened
	 * @param requestException
	 */
	public void setError(RequestException requestException) {

		if (requestException.getErrorType() != null)
			responseHeader.setErrorMessage(requestException.getErrorType()
					.getErrorMessage());
		else
			responseHeader.setErrorMessage(requestException.getCustomError());

		responseHeader.setResponseStatus(ResStatus.ERROR);
	}

	
	/**
	 * Server method to "tell" the client an error has happened
	 * @param error error code to use on the client
	 */
	public void setCustomError(String  error) {

		if (error != null)
			responseHeader.setErrorMessage(error);
		else
			responseHeader.setErrorMessage(ReqErrorTypes.REQUEST_FAILED.getErrorMessage());

		responseHeader.setResponseStatus(ResStatus.ERROR);
	}
	
	/**
	 * Simple way to tell how many items in the response
	 * @param num
	 */
	public void setTotalItemsNum(int num) {
		responseHeader.setTotalItemsNum(num);
	}

	/**
	 * The response header class
	 *
	 */
	private class MessageHeader {
		// THIS CLASS IS BEENING PARSED TO JSON OBJECT
		// DO NOT REFACTOR NAMES, DON'T ADD more MEMBERS -- need to change
		// ResParamNames and compile GWT client
		private String responseStatus = null;
		private String userContainer = null;
		private String errorMessage = null;
		private final String responseVersion = "1.0";
		private boolean hasMoreItems = false;
		private int totalItemsNum = 0;
		private boolean addPermit = true;
		private final ArrayList<ErrorFields> errorFields = new ArrayList<ErrorFields>();
		private final ArrayList<CustomHeader> customHeaders = new ArrayList<CustomHeader>();

		public String getUserContainer(){
			return this.userContainer;
		}
		
		public void setUserContainer(String userContainer){
			 this.userContainer =userContainer;
		}
		
		public void setTotalItemsNum(int num) {
			this.totalItemsNum = num;
		}

		public void setAddPermit(boolean permit) {
			this.addPermit = permit;
		}

		public String getResponseStatus() {
			return this.responseStatus;
		}

		public void addFieldError(String fieldName, String errorCode) {

			ErrorFields fieldError = new ErrorFields();
			fieldError.addFieldError(fieldName, errorCode);

			errorFields.add(fieldError);

		}

		public void addCustomeHeader(String name, String value) {

			CustomHeader customHeader = new CustomHeader();
			customHeader.addHeader(name, value);

			customHeaders.add(customHeader);

		}

		public void setResponseStatus(ResStatus status) {
			this.responseStatus = status.getStatus();
		}

		public String getErrorMessage() {
			return this.errorMessage;
		}

		public void setErrorMessage(String errorMessage) {
			this.errorMessage = errorMessage;
		}

		public boolean isHasMore() {
			return hasMoreItems;
		}

		public void setHasMore(boolean hasMore) {
			this.hasMoreItems = hasMore;
		}

	}

	/**
	 *	This class is used to store a "record" of content. later will use Gson to create
	 *	a list of JSON objects to represent content item
	 */
	public class ContentRecord {
		// THIS CLASS IS BEENING PARSED TO JSON OBJECT
		// DO NOT REFACTOR NAMES, DON'T ADD more MEMBERS 
		private Map<String, String> item = new HashMap<String, String>();
		private GadgetItemPermissions itemPermissions = new GadgetItemPermissions();
		private ArrayList<ContentRecord> subItems =null;
		private String contentName;
		

	    /**
	     * Public method to get the content item content name.
	      * @return The content name of the current record
	     */
		public String getContentName() {
			return contentName;
		}

				
		/**
	     * Public method to set the content item content name.
	     * <p>
	     * @param contentName the content name of the current record
	     */
		public void setContentName(String contentName) {
			this.contentName = contentName;
		}

		/**
	     * Public method to add a field to the content item
	     * <p>
	     * @param fieldName the content item field name
	     * @param fieldValue the content item field value
	     */
		// TODO prevent from sending fields with reserved names or sensitive info
		public void addItemField(String fieldName, String fieldValue) {
			boolean isLegal = true;
			Logger log = Logger.getLogger(this.getClass().getName());

			for (ReqParamNames param : ReqParamNames.values()) {
				if (param.getParamName().equals(fieldName)) {
					isLegal = false;
					log.warning("attempt to sent to client parameter name:"
							+ fieldName);
				}

			}

			if (isLegal)
				item.put(fieldName, fieldValue);
		}
		
		public void addShareWith(String friendId){
			
			item.put(ResParamNames.RES_SHARE_WITH.getParamName(), friendId);
		}
		
		public void addShareType(SharingType sharingType){
			
			item.put(ResParamNames.RES_SHARE_TYPE.getParamName(), sharingType.getSharingType());
		}
		
		/**
	     * Public method to add sub items to a content record
	     * <p>
	     * @return a new subItem  
	     */
		public ContentRecord getSubItem(){
			if (this.subItems == null)
				this.subItems = new ArrayList<ContentRecord>();
			ContentRecord newSubItem = new ContentRecord();
			this.subItems.add(newSubItem); 
			
			return newSubItem;
			
		}
		
		

		public void setGadgetPermissions(boolean updatePermit,
				boolean deletePermit, boolean sharePermit) {
			itemPermissions.setPermissions(updatePermit, deletePermit,
					sharePermit);
		}

		public void addItemKeyField(String fieldValue) {
			item.put(ResParamNames.RES_ITEM_KEY_PARAM_NAME.getParamName(),
					fieldValue);
		}
	}

	private class ErrorFields {

		private Map<String, String> fieldError = new HashMap<String, String>();

		public void addFieldError(String fieldName, String errorMsg) {
			fieldError.put(ResParamNames.RES_ERROR_FIELD_NAME.getParamName(),
					fieldName);
			fieldError.put(ResParamNames.RES_FIELD_ERROR_MSG.getParamName(),
					errorMsg);
		}
	}

	private class CustomHeader {

		private Map<String, String> customHeaderField = new HashMap<String, String>();

		public void addHeader(String headerName, String headerValue) {
			customHeaderField.put(
					ResParamNames.RES_CUSTOM_HEADER_NAME.getParamName(),
					headerName);
			customHeaderField.put(
					ResParamNames.RES_CUSTOM_HEADER_VALUE.getParamName(),
					headerValue);
		}
	}

	public class GadgetItemPermissions {

		private boolean updatePermit = false;
		private boolean deletePermit = false;
		private boolean sharePermit = false;

		protected GadgetItemPermissions() {

			this.updatePermit = false;
			this.deletePermit = false;
			this.sharePermit = false;
		}

		protected void setPermissions(boolean updatePermit,
				boolean deletePermit, boolean sharePermit) {

			this.updatePermit = updatePermit;
			this.deletePermit = deletePermit;
			this.sharePermit = sharePermit;
		}

	}

}
